/*
 * LinnerLayout.cpp
 *
 *  Created on: 2012-10-25
 *      Author: yangentao@gmail.com
 */
#include "Window.h"
#include "LinnerLayout.h"

//TODO padding

LinnerLayout::LinnerLayout(bool vertical_) {
	vertical = vertical_;
}
void LinnerLayout::layout(Window* parent) {
	if (vertical) {
		layoutV(parent);
	} else {
		layoutH(parent);
	}
}
/*
 * x > M/n
 * ==> x > (M-x)/(n-1)
 */

int calcAvgWidth(Vector<Window*> children, int totalWidth) {
	int oldTotal = totalWidth;
	int weightSum = 0;
	int fillCount = 0;

	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		LinnerParam* param = child->getLayoutParam()->toLinner();
		param->check();

		if (param->width.isFill()) {
			weightSum += param->widthWeight;
			++fillCount;
		}
	}
	int avg = 0;
	if (totalWidth > 0 && fillCount) {
		avg = totalWidth / (weightSum ? weightSum : fillCount);
	}
	if (avg > 0) {
		for (int i = 0; i < children.size(); ++i) {
			Window* child = children.get(i);
			LinnerParam* param = child->getLayoutParam()->toLinner();
			if (param->width.isFill()) {
				int myWidth = avg * (weightSum ? param->widthWeight : 1);
				if (param->width.minLimit > 0) {
					if (param->width.minLimit > myWidth) {
						children.remove(i);
						int newTotal = oldTotal - param->width.minLimit;
						if (newTotal > 0) {
							return calcAvgWidth(children, newTotal);
						} else {
							return 0;
						}
					}
				}
				if (param->width.maxLimit > param->width.minLimit) {
					if (myWidth > param->width.maxLimit) {
						children.remove(i);
						return calcAvgWidth(children, oldTotal - param->width.maxLimit);
					}
				}
			}
		}
	}
	return avg;
}
void LinnerLayout::layoutH(Window* parent) {
	Vector<Window*> children = parent->getChildren(true, linnerLayoutParam);
	if (children.empty()) {
		return;
	}
	Rect clientRect = parent->getClientRect();

	int clientHeight = clientRect.Height();
	int totalWidth = clientRect.Width();

	int weightSum = 0;
	int fillCount = 0;

	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		LinnerParam* param = child->getLayoutParam()->toLinner();
		param->check();

		if (param->width.isFill()) {
			weightSum += param->widthWeight;
			++fillCount;
		} else {
			totalWidth -= param->width.fixValue();
		}
		totalWidth -= param->marginLeft + param->marginRight;
	}
	int avg = calcAvgWidth(children, totalWidth);
	bool useWeight = weightSum > 0;
	int fragmentTotal = 0;
	fragmentTotal = totalWidth - avg * (useWeight ? weightSum : fillCount);
	fragmentTotal = Max(fragmentTotal, 0);
	int x = 0;
	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		LinnerParam* param = child->getLayoutParam()->toLinner();

		x += param->marginLeft;
		int width = 0;
		if (param->width.isFill()) {
			if (useWeight) {
				width = avg * param->widthWeight;
			} else {
				width = avg;
			}
			if (width < param->width.minLimit) {
				width = param->width.minLimit;
			} else if (width > param->width.maxLimit && param->width.maxLimit > param->width.minLimit) {
				width = param->width.maxLimit;
			} else {
				if (fragmentTotal > 0) {
					int delta = useWeight ? param->widthWeight : 1;
					delta = Min(delta,fragmentTotal );
					delta = Max(delta, 1);
					fragmentTotal -= delta;
					width += delta;
				}
			}
		} else {
			width = param->width.fixValue();
		}
		assert(width >=0);

		int height = 0;
		if (param->height.isFill()) {
			height = clientHeight;
		} else {
			height = param->height.fixValue();
		}
		height = Max(height, 0);
		height = Max(height, param->height.minLimit);
		if (param->height.maxLimit > 0 && height > param->height.maxLimit) {
			height = param->height.maxLimit;
		}

		int y = 0;
		if (param->ver.isTop()) {
			y = 0;
		} else if (param->ver.isBottom()) {
			y = clientHeight - height;
		} else if (param->ver.isCenter()) {
			y = (clientHeight - height) / 2;
		}
		child->moveWindow(x, y, width, height);

		x += width + param->marginRight;
	}
}

/*
 * x > M/n
 * ==> x > (M-x)/(n-1)
 */

int calcAvgHeight(Vector<Window*> children, int totalHeight) {
	int oldTotal = totalHeight;
	int weightSum = 0;
	int fillCount = 0;

	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		LinnerParam* param = child->getLayoutParam()->toLinner();
		param->check();

		if (param->height.isFill()) {
			weightSum += param->heightWeight;
			++fillCount;
		}
	}
	int avg = 0;
	if (totalHeight > 0 && fillCount) {
		avg = totalHeight / (weightSum ? weightSum : fillCount);
	}
	if (avg > 0) {
		for (int i = 0; i < children.size(); ++i) {
			Window* child = children.get(i);
			LinnerParam* param = child->getLayoutParam()->toLinner();
			if (param->height.isFill()) {
				int myHeight = avg * (weightSum ? param->heightWeight : 1);
				if (param->height.minLimit > 0) {
					if (param->height.minLimit > myHeight) {
						children.remove(i);
						int newTotal = oldTotal - param->height.minLimit;
						if (newTotal > 0) {
							return calcAvgHeight(children, newTotal);
						} else {
							return 0;
						}
					}
				}
				if (param->height.maxLimit > param->height.minLimit) {
					if (myHeight > param->height.maxLimit) {
						children.remove(i);
						return calcAvgHeight(children, oldTotal - param->height.maxLimit);
					}
				}
			}
		}
	}
	return avg;
}
void LinnerLayout::layoutV(Window* parent) {
	Vector<Window*> children = parent->getChildren(true, linnerLayoutParam);
	if (children.empty()) {
		return;
	}
	Rect clientRect = parent->getClientRect();

	int clientWidth = clientRect.Width();
	int totalHeight = clientRect.Height();

	int weightSum = 0;
	int fillCount = 0;

	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		LinnerParam* param = child->getLayoutParam()->toLinner();
		param->check();

		if (param->height.isFill()) {
			weightSum += param->heightWeight;
			++fillCount;
		} else {
			totalHeight -= param->height.fixValue();
		}
		totalHeight -= param->marginTop + param->marginBottom;
	}
	int avg = calcAvgHeight(children, totalHeight);
	bool useWeight = weightSum > 0;
	int fragmentTotal = 0;
	fragmentTotal = totalHeight- avg * (useWeight ? weightSum : fillCount);
	fragmentTotal = Max(fragmentTotal, 0);
	int y = 0;
	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		LinnerParam* param = child->getLayoutParam()->toLinner();

		y += param->marginTop;

		int height = 0;
		if (param->height.isFill()) {
			if (useWeight) {
				height = avg * param->heightWeight;
			} else {
				height = avg;
			}
			if (height < param->height.minLimit) {
				height = param->height.minLimit;
			} else if (height > param->height.maxLimit && param->height.maxLimit > param->height.minLimit) {
				height = param->height.maxLimit;
			} else {
				if (fragmentTotal > 0) {
					int delta = useWeight ? param->widthWeight : 1;
					delta = Min(delta,fragmentTotal );
					delta = Max(delta, 1);
					fragmentTotal -= delta;
					height += delta;
				}
			}
		} else {
			height = param->height.fixValue();
		}
		assert(height >=0);

		int width = 0;
		if (param->width.isFill()) {
			width = clientWidth;
		} else {
			width = param->width.fixValue();
		}
		width = Max(width, 0);
		width = Max(width, param->width.minLimit);
		if (param->width.maxLimit > 0 && width > param->width.maxLimit) {
			width = param->width.maxLimit;
		}

		int x = 0;
		if (param->hor.isLeft()) {
			x = 0;
		} else if (param->hor.isRight()) {
			x = clientWidth - width;
		} else if (param->hor.isCenter()) {
			x = (clientWidth - width) / 2;
		}
		child->moveWindow(x, y, width, height);

		y += height + param->marginBottom;
	}
}

